package org.bcncoding.appengine.droiding.server.fetch;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.tools.ant.taskdefs.LoadProperties;
import org.bcncoding.appengine.droiding.server.dao.DaoFactory;
import org.bcncoding.appengine.droiding.server.dao.ServiceProviderDao;
import org.bcncoding.appengine.droiding.server.dao.StationDao;
import org.bcncoding.appengine.droiding.server.dao.StationDataDao;
import org.bcncoding.appengine.droiding.server.dao.UserTransaction;
import org.bcncoding.appengine.droiding.server.entity.ServiceProvider;
import org.bcncoding.appengine.droiding.server.entity.Station;
import org.bcncoding.appengine.droiding.server.entity.StationData;
import org.bcncoding.appengine.droiding.server.entity.SystemType;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;

public class ZaragozaFetchServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 3087657271057936939L;

	private static final String ZARAGOZA_URL = "http://www.bizizaragoza.com/localizaciones/localizaciones.php";
	
	private static final Logger log = Logger.getLogger(ZaragozaFetchServlet.class.getName());
	
	private ServiceProviderDao serviceProviderDAO = DaoFactory.newServiceProviderDao();
	
	private StationDao stationDAO = DaoFactory.newStationDao();
	
	private ServiceProvider zaragozaProvider = null;
	
	private static final String ZARAGOZA_PROVIDER_KEY = "ZAZ";
	
	/*
	 * 41.654573 y -0.882511
	 * (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
        try {
        	if (zaragozaProvider == null)
        		loadProvider();
        	log.fine("Trying to Update data for Zaragoza Service...");
            URL url = new URL(ZARAGOZA_URL);
            URLFetchService service = URLFetchServiceFactory.getURLFetchService();
            HTTPResponse response = service.fetch(url);
            String kml = processPage(new String(response.getContent(), "ISO-8859-1"));
            processKml(kml);
            resp.getWriter().println("Done!");
            log.fine("Data for Zaragoza Service successfully updated !");
            
        } catch (MalformedURLException e) {
        	log.severe("Error retrieving data for Zaragoza Service" + e.getMessage());
        } catch (IOException e) {
        	log.warning("Connection problem retrieving data for Zaragoza Service" + e.getMessage());
        }
	}

	private void loadProvider() {
		zaragozaProvider = serviceProviderDAO.getByKey(ZARAGOZA_PROVIDER_KEY);
		if (zaragozaProvider == null) {
			zaragozaProvider = new ServiceProvider();
			zaragozaProvider.setKey(ZARAGOZA_PROVIDER_KEY);
			zaragozaProvider.setLocation("Zaragoza, Spain");
			zaragozaProvider.setName("bizi Zaragoza");
			zaragozaProvider.setType(SystemType.clearchannel);
			zaragozaProvider.setZoomLevel(17);
			zaragozaProvider.setLatitude(Double.parseDouble("41.654573"));
			zaragozaProvider.setLongitude(Double.parseDouble("-0.882511"));
			serviceProviderDAO.save(zaragozaProvider);
		}
	}
	private void processKml(String kml) {
		DocumentBuilder builder = null;
		Document doc = null;
		try {
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			doc = builder.parse(new ByteArrayInputStream(kml.getBytes("UTF-8")));
			logKml(doc);			
			XPath xpath = XPathFactory.newInstance().newXPath();
			String expression = "/kml/Document";
			NodeList placemarks = (NodeList) xpath.evaluate(expression, doc, XPathConstants.NODE);
			UserTransaction.begin();
			for (int i = 0; i < placemarks.getLength(); i++) {
				if (!"Placemark".equals(placemarks.item(i).getNodeName()))
					continue;
				NodeList elements = placemarks.item(i).getChildNodes();
				String station_description = null;
				String coordinates = null;
				for (int j = 0; j < elements.getLength(); j++) {
					Node elem = elements.item(j);
					log.finest("element: " + elem.getNodeName());
					if ("description".equals(elem.getNodeName())) {
						station_description = elem.getTextContent();
					}
					if ("Point".equals(elem.getNodeName())) {
						Node coords = elem.getFirstChild();
						coordinates = coords.getTextContent();
					}					
				}
				if (station_description != null && coordinates != null)
					processStation(station_description, coordinates);
			}
		} catch (ParserConfigurationException e) {
			log.severe("Error parsing data for Zaragoza Service" + e.getMessage());
		} catch (SAXException e) {
			log.severe("Error parsing data for Zaragoza Service" + e.getMessage());
		} catch (IOException e) {
			log.severe("Error parsing data for Zaragoza Service" + e.getMessage());
		} catch (XPathExpressionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (UserTransaction.isActive())
				UserTransaction.rollback();
		}
		
		
	}

	private void processStation(String station_description, String coordinates) {
		//TODO get UTC
		Date lastUpdate = new Date();
		String first_piece = "<div style=\"margin:10px\"><div style=\"font:bold 11px verdana;color:#cf152c;margin-bottom:10px\">";
		String second_piece = "<div style=\"margin-left:5px;float:left;font:bold 11px verdana;color:green\">";
		int start_addr = station_description.indexOf(first_piece) + first_piece.length();
		int end_addr = station_description.indexOf("</i>");
		String address = station_description.substring(start_addr, end_addr);
		String remaining = station_description.substring(end_addr + 4);
		int start_ava = remaining.indexOf(second_piece) + second_piece.length();
		int end_ava = remaining.indexOf("<br />", start_ava);
		String bikes_availability = remaining.substring(start_ava, end_ava);
		remaining = remaining.substring(end_ava + "<br />".length());
		String slot_availability = remaining.substring(0, remaining.indexOf("<br />"));
		Station station = stationDAO.getByLocation(address);
		if (station == null) {
			station = new Station();
			station.setLocation(address);
			station.setKey(address);
			station.setProvider(zaragozaProvider);
			zaragozaProvider.getStations().add(station);
			station.setData(new ArrayList<StationData>());
			
			String[] coordsArray = coordinates.split(",");
			String lng = coordsArray[0];
			String lat = coordsArray[1];
			
			station.setLatitude(Double.parseDouble(lat));
			station.setLongitude(Double.parseDouble(lng));

		}

		
		StationData sd = new StationData();
		
		station.getData().add(sd);
		sd.setAvailable(Boolean.TRUE);
		sd.setSlots(Integer.parseInt(slot_availability));
		sd.setBicycles(Integer.parseInt(bikes_availability));
		sd.setLastUpdate(lastUpdate);
		
		station.setLastUpdate(lastUpdate);
		

		log.fine("addr: [" + address + "] bikes: [" + bikes_availability + "] slots: [" + slot_availability + "] - coords: " + coordinates);
		
	}

	private String processPage(String page) {
		int start = page.indexOf("<kml xmlns=\"http://earth.google.es/kml/2.0\">");
		int end = page.indexOf("</kml>");
		end += 6;
		String kml = page.substring(start, end);
		return kml;
	}
	private void logKml(Document doc) {
        TransformerFactory tfactory = TransformerFactory.newInstance();
        Transformer serializer = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            serializer = tfactory.newTransformer();
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            serializer.transform(new DOMSource(doc), new StreamResult(bos));
            log.fine(bos.toString());
        } catch (TransformerException e) {
            log.warning("Cannot log kml !" + e.getMessage());
        }
	}
}
